/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>

#include "SqliteDatabase.h"
#include "Error.h"
#include "myassert.h"
#include "Logger.h"

#include "sqlite/sqlite3.h"


using namespace Chronos;
using namespace std;

SqliteDatabase* SqliteDatabase::createDummy(const char* sqlfile) {
    FILE *infile = fopen(sqlfile, "rb");
    if (infile == 0) {
        string errmsg = "cannot open database file '";
        errmsg += sqlfile;
        errmsg += "'";
        throw MudError(errmsg);
    }
    char buffer[4096];
    size_t size = fread(&buffer[0], 1, 4095, infile);
    buffer[size] = 0;
    fclose(infile);

    SqliteDatabase* database = new SqliteDatabase();

    if (SQLITE_OK != sqlite3_exec(database->db, &buffer[0], 0, 0, 0)) throw MudError(sqlite3_errmsg(database->db));
    return database;
}


SqliteDatabase::SqliteDatabase() {
    if (SQLITE_OK != sqlite3_open_v2(":memory:", &db, SQLITE_OPEN_READWRITE, 0))
        throw MudError(sqlite3_errmsg(db));
}


SqliteDatabase::SqliteDatabase(const char* dbfile) {
    if (SQLITE_OK != sqlite3_open_v2(dbfile, &db, SQLITE_OPEN_READWRITE, 0)) {
        throw MudError(string(sqlite3_errmsg(db)) + " '" + dbfile + "'");
    }
    
    snprintf(statement, MAX_STATEMENT, "select count(id) from players;");
    execute(statement);
    int numPlayers = atoi(getResult(0,0));
    sqlite3_free_table(results);
    LOG(INFO, "Database: SQLite version %s  using %s  (%d players)", SQLITE_VERSION, dbfile, numPlayers);
}


SqliteDatabase::~SqliteDatabase() {
    int result = sqlite3_close(db);
    if (sqlite3_memory_used() != 0) LOG(WARN, "Database: %llu bytes unfreed memory!", sqlite3_memory_used());
    if (result) LOG(WARN, "Database: error closing database (%d)", result);
    LOG(DEBUG, "Database: closed");
}


bool SqliteDatabase::hasPlayer(const string& name) {
    snprintf(statement, MAX_STATEMENT, "select name from players where name='%s' LIMIT 1;", name.c_str());
    execute(statement);
    sqlite3_free_table(results);
    return (rows != 0);
}


bool SqliteDatabase::checkPasswd(const string& name, const string& passwd) {
    snprintf(statement, MAX_STATEMENT, "select password from players where name='%s' LIMIT 1;", name.c_str());
    execute(statement);
    ASSERT_NOT_EQUAL(0, rows);
     
    bool ok = (passwd == results[1]);
    sqlite3_free_table(results);
    return ok;
}


void SqliteDatabase::addPlayer(const string& name, const string& password) {
    snprintf(statement, MAX_STATEMENT, "insert into players (name, password) values ('%s', '%s');", name.c_str(), password.c_str());
    execute(statement);
    int id = sqlite3_last_insert_rowid(db);
    sqlite3_free_table(results);
    LOG(DEBUG, "Database: adding player %s (id %d)", name.c_str(), id);
}


void SqliteDatabase::query(const char* statement) {
    execute(statement);
    sqlite3_free_table(results);
}


void SqliteDatabase::query(const char* statement, Result& result) {
    execute(statement);
    ASSERT_EQUAL(1, rows);
    for (int i=0; i<columns; i++) result.add(getResult(0, i));
    sqlite3_free_table(results);
}


void SqliteDatabase::savePlayerAttribute(int playerID, const char* attribute, const string& value) {
    snprintf(statement, MAX_STATEMENT, "update players set %s='%s' where id=%d;", attribute, value.c_str(), playerID);
    execute(statement);
    sqlite3_free_table(results);
}


string SqliteDatabase::getPlayerAttribute(int playerID, const char* attribute) {
    snprintf(statement, MAX_STATEMENT, "select %s from players where id=%d LIMIT 1;", attribute, playerID);
    execute(statement);
    ASSERT_NOT_EQUAL(0, rows);
    string result = results[1];
    sqlite3_free_table(results);
    return result;
}


int SqliteDatabase::getPlayerIntAttribute(int playerID, const char* attribute) {
    snprintf(statement, MAX_STATEMENT, "select %s from players where id=%d LIMIT 1;", attribute, playerID);
    execute(statement);
    ASSERT_NOT_EQUAL(0, rows);
    int result = atoi(results[1]);
    sqlite3_free_table(results);
    return result;
}

void SqliteDatabase::execute(const char* statement_) {
    if (SQLITE_OK!=sqlite3_get_table(db, statement_, &results, &rows, &columns, 0)) {
        LOG(ERROR, "DATABASE ERROR: %s (statement=[%s]\n", sqlite3_errmsg(db), statement_);
        ASSERT_FAIL();
    }
}


char* SqliteDatabase::getResult(int row, int column) const {
    return results[(row+1)*columns + column];
}

